Lambda@Edge の設置によって出力されるログは2種類ある

Lambda@Edge の設置によって出力されるログは2種類ある

実行ロールもそれぞれ異なるのでご注意ください
Clock Icon2024.08.30

こんにちは、なおにしです。

Lambda@Edgeのログ出力を検証している際、出力されるログについて調査したのでご紹介します。

はじめに

Lambda@Edge のログ出力先についてはドキュメントに以下のとおり記載があります。

Lambda@Edge は、関数ログを CloudWatch Logs に自動的に送信し、関数が実行される AWS リージョン にログストリームを作成します。

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/edge-functions-logs.html

つまり、CloudFront にデプロイされたLambda(Lambda@Edge)が実行された地域(エッジロケーション)に該当するリージョンにログが出力される挙動となります。

こちらの挙動については弊社ブログでも以下のとおり既に取り上げられています。

https://dev.classmethod.jp/articles/where-is-the-lambda-edge-log/

https://dev.classmethod.jp/articles/tsnote-lambda-edge-log-region/

しかし、上記ブログを見ていただくとそれぞれ対象としているロググループ名が異なることが分かります。

  1. /aws/lambda/us-east-1.function-name

  2. /aws/cloudfront/LambdaEdge/DistributionId

どちらともLambda@Edge を使用した際に出力されるログですが、用途が異なります。

Lambda@Edge の実行ログ

1つ目のログは実際にLambda@Edgeが特定のエッジロケーションで正常に実行されることで、コード内で定義されているログ出力イベントに基づいて出力されるものです。

例えばNode.js であれば、以下のようにconsoleオブジェクトで定義されたログ文字列が各リージョンの/aws/lambda/us-east-1.function-nameに出力されます。

exports.handler = async function(event, context) {
  console.log("ENVIRONMENT VARIABLES\n" + JSON.stringify(process.env, null, 2))
  console.info("EVENT\n" + JSON.stringify(event, null, 2))
  console.warn("Event not processed.")
  return context.logStreamName
}

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/nodejs-logging.html

CloudFront が出力するエラーログ

2つ目のログは、デプロイされたLambda@Edge がエッジロケーションで実行されようとした際、コード自体に問題があって処理に失敗することでCloudFront が出力するエラーログです。

こちらのログに関する記載は以下のとおりドキュメントにあります。

Lambda が CloudFront に無効なレスポンスを返すと、Lambda 関数が実行されるリージョンで CloudFront が CloudWatch にプッシュするログファイルに、エラーメッセージが書き込まれます。

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-testing-debugging.html#lambda-edge-testing-debugging-troubleshooting-invalid-responses

例えば、Lambda作成時にランタイムを「Node.js 20.x」に指定した状態で上記のjsコードを実行すると、以下のとおりエラーが発生します。

"errorType": "ReferenceError",
"errorMessage": "exports is not defined in ES module scope",

これは、ESモジュール方式のファイル(.mjsファイル)に、CommonJSモジュール方式のコードを記述して実行したためです。

このような状態のままLambda@Edge としてCloudFront にデプロイを行い、アクセスがきてLambda@Edge がトリガーされた時に、エラーが/aws/cloudfront/LambdaEdge/DistributionIdに出力されます。

実際に出力されるエラー内容としては、例えば今回の検証では以下のようなログが出力されました。

  • JSONの出力形式が不正な場合

ERROR Validation error: The Lambda function returned an invalid json output, parsed json should be an object type.

  • ヘッダー操作が不正な場合

ERROR Validation error: Lambda function result failed validation, the function tried to delete read-only header, headerName : Transfer-Encoding.

それでは実際にLambda@Edge でログを出力させてみた結果を見ていきます。

やってみた

Lambda@Edgeとしてデプロイする関数はバージニア北部(us-east-1)リージョンで作成する必要があります。今回は「test-edge-log-output」という名前で作成しました。

作成にあたっては以下のドキュメントが参考になります。

Lambda@Edge を作成する手順については上記のとおりなので割愛しますが、共通して注意する箇所は実行ロールの設定です。

デフォルトでは以下のように[基本的な Lambda アクセス権限で新しいロールを作成]が選択されています。

20240830_naonishi_lambda-edge-logs-role_1.png

上記を選択したまま作成されたロールにアタッチされるポリシーの内容は以下のとおりです。

AWSLambdaBasicExecutionRole-xxxxxx
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:us-east-1:AccountId:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:us-east-1:AccountId:log-group:/aws/lambda/function-name:*"
            ]
        }
    ]
}

ログ出力に関するアクションが定義されていますが、バージニア北部(us-east-1)のリージョンに対してのみ許可しています。

一方、ドキュメントに記載のとおり[AWS ポリシーテンプレートから新しいロールを作成] を選択して、ポリシーテンプレートとして[基本的な Lambda@Edge のアクセス権限 (CloudFront トリガーの場合)]を適用した場合に作成されたロールにアタッチされるポリシーの内容は以下のとおりです。

20240830_naonishi_lambda-edge-logs-role_2.png

AWSLambdaEdgeExecutionRole-xxxxxx
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:*:*:*"
            ]
        }
    ]
}

全リージョンに対するログ出力が許可されています。つまり、複数リージョンに対するログ出力を行うLambda@Edge用の関数を作成する場合、上記のとおりログ出力を許可するポリシーを追加しないと/aws/lambda/us-east-1.function-nameにログが出力されないことにご注意ください。

一方、CloudFront が出力する/aws/cloudfront/LambdaEdge/DistributionIdについては、Lambda@Edge 用のサービスリンクロールが使用されます。

Lambda@Edge は IAM サービスリンクロールを使用します。サービスにリンクされたロールは、サービスに直接リンクされた一意のタイプの IAM ロールです。サービスにリンクされたロールは、サービスによって事前定義されており、お客様の代わりにサービスから他の AWS サービスを呼び出す必要のあるアクセス許可がすべて含まれています。

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-permissions.html#using-service-linked-roles

具体的に使用されるロール名は「AWSServiceRoleForCloudFrontLogger」で、アタッチされているポリシーの内容は以下のとおりです。

AWSCloudFrontLogger
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:log-group:/aws/cloudfront/*"
        }

こちらも同様に全リージョンに対するログ出力が許可されています。私は最初Lambda@Edgeで出力されるログは全て「AWSServiceRoleForCloudFrontLogger」ロールを使用すると勘違いしていたのですが、実際はLambda で設定されたサービスロールと上記のサービスリンクロールをそれぞれ使用しており、それぞれで全リージョンに対するログ出力を許可しているという形になります。

https://dev.classmethod.jp/articles/iam-how-to-identify-service-linked-roles/

Lambda@Edge の実行状況とログ出力はCloudFront の[テレメトリー] - [モニタリング] - [Lambda@Edge]から見ると全リージョンをまたいで確認しやすくなります。

20240830_naonishi_lambda-edge-logs-role_3.jpg

[関数ログを表示]から各リージョンにおけるCloudWatch Logs の対象ロググループ/aws/lambda/us-east-1.function-nameに遷移できます。

20240830_naonishi_lambda-edge-logs-role_4.jpg

例としてオレゴン(us-west-2)リージョンのCloudWatch を開くと、以下のとおり自動的にロググループが作成されていました。

20240830_naonishi_lambda-edge-logs-role_5.jpg

まとめ

Lambda@Edge のログ出力に関して、出力されるログの種類と実行ロールについて確認しました。目的のLambda@Edge のログが見当たらない時などに本記事の内容もご確認いただければと思います。

本記事がどなたかのお役に立てれば幸いです。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.